﻿@page "/JsonEditor"
@using System.Text.Json
@using System.Text.Encodings.Web
@using System.Linq
@inject IJSRuntime JS
@inject NavigationManager NavigationManager
<PageTitle>JSON 编辑器</PageTitle>

<div class="json-editor-container">
	<div class="header">
		<h1>JSON 编辑器</h1>
		<div class="actions">
			<button class="btn btn-secondary" @onclick="GoBack">
				<i class="fas fa-arrow-left"></i> 返回
			</button>
		</div>
	</div>

    <!-- 新增：格式化JSON文件上传与进度条 -->
    <div class="file-format-row">
        <InputFile OnChange="OnFileSelected" accept=".txt" />
        <progress max="100" value="@uploadProgress" style="width:120px;vertical-align:middle;" hidden="@(uploadProgress==0||uploadProgress==100)"></progress>
        <span>@(isUploading ? $"上传中...{uploadProgress}%" : null)</span>
        <button class="btn btn-success" @onclick="FormatUploadedJson" disabled="@(uploadedJson == null || isFormatting || isUploading)">格式化JSON文件</button>
        <progress max="100" value="@formatProgress" style="width:120px;vertical-align:middle;" hidden="@(formatProgress==0||formatProgress==100)"></progress>
        <span>@(isFormatting ? $"格式化中...{formatProgress}%" : null)</span>
        <button class="btn btn-info" @onclick="DownloadFormattedJson" disabled="@(string.IsNullOrEmpty(formattedJson))">下载格式化结果</button>
    </div>

	<div class="editor-layout">
		<!-- JSON输入区域 -->
		<div class="editor-section">
			<div class="section-header">
				<label for="jsonInput">JSON 输入</label>
				<div class="section-actions">
					<button class="btn btn-sm btn-icon" @onclick="FormatJson" title="格式化">
						<i class="fas fa-align-left"></i>
					</button>
					<button class="btn btn-sm btn-icon" @onclick="MinifyJson" title="压缩">
						<i class="fas fa-compress"></i>
					</button>
					<button class="btn btn-sm btn-icon" @onclick="ClearJson" title="清空">
						<i class="fas fa-trash"></i>
					</button>
				</div>
			</div>
			<textarea id="jsonInput" @bind="JsonInput" @bind:event="oninput"
				  class="json-input @(IsJsonValid ? "" : "invalid")"></textarea>
			@if (!IsJsonValid) {
				<div class="error-message">
					<i class="fas fa-exclamation-circle"></i> JSON格式无效
				</div>
			}
		</div>

		<!-- 树形结构区域 -->
		<div class="editor-section">
			<div class="section-header">
				<label>树形结构视图</label>
				<div class="section-actions">
					<button class="btn btn-sm btn-icon" @onclick="ExpandAll" title="全部展开">
						<i class="fas fa-expand"></i>
					</button>
					<button class="btn btn-sm btn-icon" @onclick="CollapseAll" title="全部折叠">
						<i class="fas fa-compress"></i>
					</button>
				</div>
			</div>
			<div id="tree_editor" class="tree-editor"></div>
		</div>
	</div>

	<div class="footer-actions">
		<button class="btn btn-primary" @onclick="() => ConvertToTree()">
			<i class="fas fa-sync-alt"></i> 转换为树结构
		</button>
@* 		<button class="btn btn-success" @onclick="SaveJson" disabled="@(!IsJsonValid)">
			<i class="fas fa-save"></i> 保存JSON
		</button> *@
	</div>

	@if (showSuccess) {
		<div class="alert alert-success">
			<i class="fas fa-check-circle"></i> JSON保存成功！
		</div>
	}

	@* 历史记录区域 *@
	<div class="history-section">
		<h3>历史记录</h3>
		<div class="history-list">
			@foreach (var item in history)
			{
				<div class="history-item">
					<div class="history-json-preview" title="点击切换到此JSON" @onclick="() => LoadHistory(item)">
						<pre>@item.JsonPreview</pre>
					</div>
					<input class="history-remark" placeholder="备注..." value="@item.Remark" @onchange="(e) => UpdateRemark(item, e.Value?.ToString())" />
				</div>
			}
		</div>
	</div>
</div>

@code {
	private string JsonInput { get; set; } = @"{
  ""name"": ""萧炎"",
  ""gender"": ""男"",
  ""Level"": ""斗之气三段""
}";
	private bool IsJsonValid { get; set; } = true;
	private bool showSuccess = false;

    // 文件上传与格式化相关字段
    private IBrowserFile? uploadedFile;
    private string? uploadedJson;
    private string? formattedJson;
    private int formatProgress = 0;
    private bool isFormatting = false;
    // 新增：上传进度
    private int uploadProgress = 0;
    private bool isUploading = false;

    // 历史记录结构
    private class HistoryItem
    {
        public string Json { get; set; } = string.Empty;
        public string Remark { get; set; } = string.Empty;
        public string JsonPreview => Json.Length > 120 ? Json.Substring(0, 120) + "..." : Json;
    }
    private List<HistoryItem> history = new();

    private async Task OnFileSelected(InputFileChangeEventArgs e)
    {
        uploadedFile = e.File;
        formattedJson = null;
        formatProgress = 0;
        uploadedJson = null;
        uploadProgress = 0;
        isUploading = true;
        StateHasChanged();
        if (uploadedFile != null)
        {
            var buffer = new byte[81920]; // 80KB块
            using var stream = uploadedFile.OpenReadStream(maxAllowedSize: 20 * 1024 * 1024); // 20MB限制
            using var ms = new MemoryStream();
            long total = uploadedFile.Size;
            long read = 0;
            int n;
            while ((n = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                await ms.WriteAsync(buffer, 0, n);
                read += n;
                uploadProgress = (int)(read * 100 / total);
                StateHasChanged();
            }
            ms.Seek(0, SeekOrigin.Begin);
            using var reader = new StreamReader(ms);
            uploadedJson = await reader.ReadToEndAsync();
            uploadProgress = 100;
        }
        isUploading = false;
        StateHasChanged();
    }

    private async Task FormatUploadedJson()
    {
        if (string.IsNullOrWhiteSpace(uploadedJson)) return;
        isFormatting = true;
        formatProgress = 0;
        formattedJson = null;
        StateHasChanged();
        try
        {
            // 分步格式化：按行分块处理，模拟进度
            var json = uploadedJson.Trim();
            using var doc = JsonDocument.Parse(json);
            var pretty = JsonSerializer.Serialize(doc.RootElement, new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
            int steps = 10;
            for (int i = 1; i <= steps; i++)
            {
                formatProgress = i * 100 / steps;
                StateHasChanged();
                await Task.Delay(80); // 模拟耗时
            }
            formattedJson = pretty;
            formatProgress = 100;
        }
        catch (Exception ex)
        {
            formattedJson = $"格式化失败: {ex.Message}";
            formatProgress = 0;
        }
        isFormatting = false;
        StateHasChanged();
    }

    private async Task DownloadFormattedJson()
    {
        if (string.IsNullOrEmpty(formattedJson)) return;
        var bytes = System.Text.Encoding.UTF8.GetBytes(formattedJson);
        var base64 = Convert.ToBase64String(bytes);
        var fileName = uploadedFile?.Name?.Replace(".txt", "_formatted.json") ?? "formatted.json";
        await JS.InvokeVoidAsync("BlazorDownloadFile", fileName, "application/json", base64);
    }

	protected override async Task OnAfterRenderAsync(bool firstRender) {
		if (firstRender) {
			await JS.InvokeVoidAsync("eval", @"
                window.renderTree = function (json) {
                    try {
                        if (window.treeEditorInstance) {
                            window.treeEditorInstance.destroy();
                            window.treeEditorInstance = null;
                        }
                        window.treeEditorInstance = new JSONEditor(document.getElementById('tree_editor'), {
                            mode: 'tree',
                            modes: ['text', 'code', 'tree'],
                            search: true,
                            history: true,
                            onError: function (err) {
                                console.error('JSONEditor error:', err);
                            }
                        });
                        window.treeEditorInstance.set(json);
                        window.treeEditorInstance.expandAll();
                    } catch (e) {
                        console.error('JSONEditor error:', e);
                    }
                };
            ");

			// 初始渲染树结构
			await ConvertToTree();
		}
	}

	private void GoBack() {
		NavigationManager.NavigateTo("/");
	}

	private async Task FormatJson() {
		try {
			var options = new JsonSerializerOptions {
				Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
				WriteIndented = true
			};
			var parsed = System.Text.Json.JsonDocument.Parse(JsonInput);
			JsonInput = System.Text.Json.JsonSerializer.Serialize(parsed.RootElement, options);
			IsJsonValid = true;
		} catch {
			IsJsonValid = false;
		}
	}

	private async Task MinifyJson() {
		try {
			var options = new JsonSerializerOptions {
				Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
			};
			var parsed = System.Text.Json.JsonDocument.Parse(JsonInput);
			JsonInput = System.Text.Json.JsonSerializer.Serialize(parsed.RootElement,options);
			IsJsonValid = true;
		} catch {
			IsJsonValid = false;
		}
	}

	private void ClearJson() {
		JsonInput = string.Empty;
		IsJsonValid = true;
	}

	private async Task ConvertToTree(bool addHistory = true) {
		try {
			var parsed = System.Text.Json.JsonDocument.Parse(JsonInput);
			var json = parsed.RootElement.GetRawText();
			await JS.InvokeVoidAsync("renderTree", System.Text.Json.JsonSerializer.Deserialize<object>(json));
			IsJsonValid = true;
			// 仅主动转换时添加历史记录
			if (addHistory && !history.Any(p => p.Json == JsonInput))
				history.Add(new HistoryItem { Json = JsonInput });
		} catch {
			IsJsonValid = false;
		}
	}

	private async void LoadHistory(HistoryItem item)
{
    JsonInput = item.Json;
    StateHasChanged();
    await ConvertToTree(false);
}

    private void UpdateRemark(HistoryItem item, string? remark)
    {
        item.Remark = remark ?? string.Empty;
    }

	private async Task ExpandAll() {
		await JS.InvokeVoidAsync("eval", "if (window.treeEditorInstance) window.treeEditorInstance.expandAll();");
	}

	private async Task CollapseAll() {
		await JS.InvokeVoidAsync("eval", "if (window.treeEditorInstance) window.treeEditorInstance.collapseAll();");
	}

	private async Task SaveJson() {
		if (!IsJsonValid) return;

		// 这里可以添加保存到服务器或本地存储的逻辑
		showSuccess = true;
		await InvokeAsync(StateHasChanged);
		await Task.Delay(2000);
		showSuccess = false;
		StateHasChanged();
	}
}
<style>
	/* 全局样式 */
	:root {
		--primary: #4361ee;
		--secondary: #3f37c9;
		--success: #4cc9f0;
		--danger: #f72585;
		--light: #f8f9fa;
		--dark: #212529;
		--border: #dee2e6;
		--shadow: rgba(0, 0, 0, 0.1);
	}

	.json-editor-container {
		max-width: 1600px;
		margin: 0 auto;
		padding: 20px;
	}

	.header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		margin-bottom: 24px;
		padding-bottom: 15px;
		border-bottom: 1px solid var(--border);
	}

	h1 {
		font-size: 28px;
		font-weight: 600;
		color: var(--primary);
		margin: 0;
	}

    .file-format-row {
        display: flex;
        gap: 12px;
        align-items: center;
        margin-bottom: 18px;
    }

	.editor-layout {
		display: flex;
		gap: 24px;
		margin-bottom: 24px;
	}

	.editor-section {
		flex: 1;
		display: flex;
		flex-direction: column;
		background: white;
		border-radius: 8px;
		box-shadow: 0 4px 12px var(--shadow);
		overflow: hidden;
	}

	.section-header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 12px 16px;
		background-color: #f8f9fa;
		border-bottom: 1px solid var(--border);
	}

		.section-header label {
			font-weight: 600;
			color: var(--dark);
			margin: 0;
		}

	.section-actions {
		display: flex;
		gap: 8px;
	}

	.json-input {
		flex: 1;
		padding: 16px;
		border: none;
		resize: none;
		font-family: 'Fira Code', 'Consolas', monospace;
		font-size: 14px;
		line-height: 1.5;
		outline: none;
	}

		.json-input.invalid {
			background-color: #fff5f5;
		}

	.tree-editor {
		height: 500px;
		width: 100%;
	}

	.error-message {
		padding: 8px 16px;
		background-color: #fff5f5;
		color: var(--danger);
		font-size: 14px;
		display: flex;
		align-items: center;
		gap: 8px;
	}

	.footer-actions {
		display: flex;
		justify-content: center;
		gap: 16px;
		margin-top: 20px;
	}

	.btn {
		padding: 10px 20px;
		border-radius: 6px;
		border: none;
		font-weight: 500;
		cursor: pointer;
		display: flex;
		align-items: center;
		gap: 8px;
		transition: all 0.2s ease;
	}

		.btn:hover {
			transform: translateY(-2px);
			box-shadow: 0 4px 8px var(--shadow);
		}

		.btn:active {
			transform: translateY(0);
		}

	.btn-primary {
		background-color: var(--primary);
		color: white;
	}

		.btn-primary:hover {
			background-color: var(--secondary);
		}

	.btn-secondary {
		background-color: #e9ecef;
		color: var(--dark);
	}

		.btn-secondary:hover {
			background-color: #dee2e6;
		}

	.btn-success {
		background-color: #06d6a0;
		color: white;
	}

		.btn-success:hover {
			background-color: #05b890;
		}

    .btn-info {
        background-color: #17a2b8;
        color: #fff;
    }

	.btn-sm {
		padding: 6px 10px;
		font-size: 13px;
	}

	.btn-icon {
		padding: 6px 10px;
		min-width: 36px;
		justify-content: center;
	}

	.alert {
		padding: 12px 16px;
		border-radius: 6px;
		margin-top: 20px;
		display: flex;
		align-items: center;
		gap: 10px;
		animation: fadeIn 0.3s;
	}

	.alert-success {
		background-color: #d4edda;
		color: #155724;
		border: 1px solid #c3e6cb;
	}

	@@keyframes fadeIn {
		from {
			opacity: 0;
			transform: translateY(10px);
		}

		to {
			opacity: 1;
			transform: translateY(0);
		}
	}

	/* 响应式设计 */
	@@media (max-width: 992px) {
		.editor-layout {
			flex-direction: column;
		}

		.tree-editor, .json-input {
			height: 400px;
		}
	}

	@@media (max-width: 576px) {
		.header {
			flex-direction: column;
			align-items: flex-start;
			gap: 10px;
		}

		.footer-actions {
			flex-direction: column;
		}

		.btn {
			width: 100%;
			justify-content: center;
		}
	}

	.history-section {
		margin-top: 32px;
		background: #f8f9fa;
		border-radius: 8px;
		padding: 16px;
		box-shadow: 0 2px 8px var(--shadow);
	}
	.history-list {
		display: flex;
		flex-direction: column;
		gap: 12px;
	}
	.history-item {
		display: flex;
		align-items: center;
		gap: 12px;
		background: #fff;
		border-radius: 6px;
		padding: 8px 12px;
		box-shadow: 0 2px 4px var(--shadow);
	}
	.history-json-preview {
		flex: 1;
		cursor: pointer;
		font-family: 'Fira Code', monospace;
		font-size: 13px;
		background: #f1f3f4;
		border-radius: 4px;
		padding: 6px;
		overflow-x: auto;
		max-width: 400px;
	}
	.history-remark {
		min-width: 120px;
		padding: 4px 8px;
		border-radius: 4px;
		border: 1px solid var(--border);
		font-size: 13px;
	}
</style>
